﻿using System.Linq;
using System.ComponentModel;
using System.Modules.Utility.DTO.ULS.EventArguments;
using System.Text;
using System.Threading;
using System.IO;
using System.Modules.Constants;

namespace System.Modules.Utility.DTO.ULS.Monitoring
{
    class FileTail : IDisposable
    {
        #region Fields
        readonly string[] _seperators = { EscapeCharacter.CARRIAGE_RETURN, EscapeCharacter.NEW_LINE };
        BackgroundWorker _worker;
        string _filePath;
        readonly ManualResetEvent _stopSync;
        #endregion

        #region Events
        public event EventHandler<LineDiscoveredEventArgs> LineDiscovered = delegate { };
        #endregion

        #region Constructor
        public FileTail()
        {
            _stopSync = new ManualResetEvent(true);
        }
        #endregion

        #region Properties
        public bool IsBusy
        {
            get { return _worker.IsBusy; }
        }
        #endregion

        #region Methods
        public void Start(string path)
        {
            _filePath = path;
            _stopSync.Reset();
            _worker = new BackgroundWorker();
            _worker.WorkerReportsProgress = true;
            _worker.WorkerSupportsCancellation = true;
            _worker.DoWork += worker_DoWork;
            _worker.ProgressChanged += worker_ProgressChanged;
            _worker.RunWorkerAsync();
        }

        public void Stop()
        {
            if (_worker != null)
            {
                _worker.CancelAsync();
                _stopSync.WaitOne();
            }
        }
        #endregion

        #region Event Handlers
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            using (var stream = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using (var reader = new StreamReader(stream))
                {
                    //skip the first row...
                    reader.ReadToEnd();
                    var dataCollector = new StringBuilder();
                    while (!_worker.CancellationPending)
                    {
                        Thread.Sleep(1000);
                        if (reader.EndOfStream)
                            continue;
                        dataCollector.Append(reader.ReadToEnd());
                        var data = dataCollector.ToString();
                        var lines = data.Split(_seperators, StringSplitOptions.RemoveEmptyEntries);
                        data = data.EndsWith(EscapeCharacter.NEW_LINE) ? String.Empty : lines.Last();
                        var validLines = string.IsNullOrEmpty(data) ? lines.Length : lines.Length - 1;
                        foreach (var line in lines.Take(validLines)) _worker.ReportProgress(0, line);
                    }
                }
            }
            _stopSync.Set();
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.UserState != null)
            {
                LineDiscovered(this, new LineDiscoveredEventArgs { Line = e.UserState.ToString() }); 
            }
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {
            if (_worker != null)
            {
                Stop();
                _worker.Dispose();
            }
        }
        #endregion
    }
}
